import { useIntl } from 'umi';
import { FC, Key, ReactNode, useEffect, useState } from 'react';
import { merge } from 'lodash';
import { Spin, Tree, Input, TreeProps } from 'antd';
import { getTempData } from '@/biz-components/_common/wrapXhr';
import { arrayToNestJson } from '@ey-utils/utils/utils';

import { TreeDataItem } from '@/biz-components/hooks/interface';
import useTreeData from '@/biz-components/hooks/useTreeData';

const { Search } = Input;

export interface AsyncTreeHooksProps
  extends Omit<
    TreeProps,
    'treeData' | 'loadData' | 'onExpand' | 'expandedKeys' | 'autoExpandParent'
  > {
  showSearch?: boolean;
  checkDisabled?: (item: any) => boolean;
  api: ApiConfig;
  parentKey?: string;
  parentKeyPath?: string[];
  childParams?: DynamicObjectAny;
  placeholder?: string;
}

const AsyncTreeHooks: FC<AsyncTreeHooksProps> = ({
  showSearch,
  checkDisabled,
  api,
  parentKeyPath,
  parentKey,
  childParams,
  placeholder,
  ...restProps
}) => {
  const { formatMessage } = useIntl();

  const { params, url, method } = api || {};

  const [loading, setLoading] = useState(false);

  const [treeData, setTreeData] = useState<TreeDataItem[]>([]);

  const [expandedKeys, setExpandedKeys] = useState<Key[]>([]);

  const [autoExpandParent, setAutoExpandParent] = useState(false);

  const [keyword, setKeyword] = useState<string | undefined>(undefined);

  const updateTreeData = (result: any[], parentId: string) => {
    setTreeData((data) => {
      const newData = result?.map((item) => {
        const { name, code } = item;
        return {
          pId: parentId,
          title: name,
          value: code,
          disabled: checkDisabled?.(item) || false,
          ...item,
          id: item[parentKey || 'id'],
        };
      });
      return data.concat(newData);
    });
  };

  const search = () => {
    setLoading(true);

    getTempData({
      url,
      params,
      type: method,
    })
      .then((res: any) => {
        updateTreeData(res, '0');
      })
      .finally(() => {
        setLoading(false);
      });
  };

  const loadData = (record: any) => {
    const id = record[parentKey || 'id'];

    return getTempData({
      type: method,
      params: merge(arrayToNestJson(parentKeyPath || [], id), childParams),
      url,
    }).then((res: any) => {
      updateTreeData(res, id);
    });
  };

  const handleSearch = (value: string) => {
    setKeyword(value);
    if (!value) return;

    setAutoExpandParent(true);

    setExpandedKeys(
      treeData
        .filter((item: TreeDataItem) => {
          // @ts-ignore
          return item.title?.indexOf(value) > -1;
        })
        .map((item: TreeDataItem) => {
          return item.pId;
        }),
    );
  };

  const handleExpand = (expandKeys: Key[]) => {
    setAutoExpandParent(false);
    setExpandedKeys(expandKeys);
  };

  const getTreeNodeTitle = (node: TreeDataItem): ReactNode => {
    const { name } = node;
    if (keyword) {
      const index = name.indexOf(keyword);
      const beforeStr = name.substr(0, index);
      const afterStr = name.substr(index + keyword.length);

      if (index > -1) {
        return (
          <span>
            {beforeStr}
            <span style={{ color: '#f50' }}>{keyword}</span>
            {afterStr}
          </span>
        );
      }
    }

    return <span>{name}</span>;
  };

  useEffect(() => {
    if (!(url && method)) return;

    search();
  }, [api]);

  const mergedTreeData = useTreeData(treeData, {
    getLabelProp: getTreeNodeTitle,
    keyword,
  });

  return (
    <Spin spinning={loading} tip={formatMessage({ id: 'loading' })}>
      {showSearch ? (
        <Search
          style={{ marginBottom: 8 }}
          placeholder={formatMessage({
            id: placeholder,
          })}
          onSearch={handleSearch}
          allowClear
        />
      ) : null}

      <Tree
        treeData={mergedTreeData}
        showLine
        loadData={loadData}
        onExpand={handleExpand}
        expandedKeys={expandedKeys}
        autoExpandParent={autoExpandParent}
        {...restProps}
      />
    </Spin>
  );
};

AsyncTreeHooks.defaultProps = {
  placeholder: 'tree.search.placeholder',
  showSearch: true,
  parentKey: 'code',
};

export default AsyncTreeHooks;
